package zx.lrn.notes.basic_grammer.about_collections;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.stream.IntStream;

/**
 * @ClassName: BlockingQueueDemo
 * @Description: 线程安全-阻塞队列demo
 * 阻塞队列（blocking-queue）是一种线程安全的队列，安全之处在于向满队列插入元素会阻塞，直到队列元素被取出出现空闲位置，向空队列取出元素同理；
 * 阻塞队列有几种常用实现：
 *  LinkedBlockingQueue：双端队列实现，容量没有上界
 *  ArrayBlockingQueue：有数组实现，所以构造时需要指定初始容量
 *  PriorityBlockingQueue：优先级队列，不在是FIFO结构，每次取出的元素是优先级最高的元素
 * @Author: zhaoxuan
 * @Date: 2020/3/9 23:50
 **/
public class BlockingQueueDemo {
    private static ArrayBlockingQueue<Path> paths = new ArrayBlockingQueue<>(5);
    private static final Path DUMMY = Paths.get("");

    public static void main(String[] args) throws Exception {
        URL txtsUrl = BlockingQueueDemo.class.getResource("/txt_sources");
        Path topest = new File(txtsUrl.toURI()).toPath();
        // 启动线程获取到所有待搜索的txt路径
        new Thread(() -> {
            try {
                enumerateFiles(topest);
                // DUMMY作为结束标识，防止文件全部搜索完成后出现搜索线程空等
                paths.put(DUMMY);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        // 多线程搜索文件中的java关键字
        IntStream.rangeClosed(1, 10).forEach(n -> {
            new Thread(() -> {
                try {
                    // 此处用take是为了再
                    Path targetTxt = paths.take();
                    if (targetTxt == DUMMY) {
                        paths.put(DUMMY);
                        return;
                    }
                    String fileName = targetTxt.getFileName().toString();
                    int lineNumber = 0;
                    for (String line : Files.readAllLines(targetTxt)) {
                        lineNumber++;
                        if (line.equals("java")) {
                            System.out.println(fileName + " : " + lineNumber);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        });
    }

    /**
     * 递归搜索某个目录下的所有子文件
     * @param path
     * @throws IOException
     * @throws InterruptedException
     */
    private static void enumerateFiles(Path path) throws IOException, InterruptedException {
        for (Path p : Files.list(path).toArray(Path[]::new)) {
            if (!Files.isDirectory(p)) {
                // 此处用put是因为队列长度有限，如果文件过多则应该阻塞等待，否则会有遗漏
                BlockingQueueDemo.paths.put(p);
            } else {
                enumerateFiles(p);
            }
        }
    }
}
